home *** CD-ROM | disk | FTP | other *** search
/ BCI NET / BCI NET Dec 94.iso / archives / programming / source / fbm12s.lha / fbham.c < prev    next >
C/C++ Source or Header  |  1994-07-18  |  15KB  |  591 lines

  1. /*****************************************************************
  2.  * fbham.c: FBM Release 1.0 25-Feb-90 Michael Mauldin
  3.  *
  4.  * Copyright (C) 1989,1990 by C. Harald Koch & Michael Mauldin.
  5.  * Permission is granted to use this file in whole or in part for
  6.  * any purpose, educational, recreational or commercial, provided
  7.  * that this copyright notice is retained unchanged.  This software
  8.  * is available to all free of charge by anonymous FTP and in the
  9.  * UUNET archives.
  10.  *
  11.  * fbham.c: Write a 24bit RGB file as an Amiga HAM IFF file
  12.  *
  13.  * USAGE
  14.  *     % fbham < image1 > image2
  15.  *
  16.  * EDITLOG
  17.  *    LastEditDate = Mon Jun 25 00:56:54 1990 - Michael Mauldin
  18.  *    LastFileName = /usr2/mlm/src/misc/fbm/fbham.c
  19.  *
  20.  * HISTORY
  21.  * 13-Jul-89  Michael Mauldin (mlm) at Carnegie Mellon University
  22.  *    Beta release (version 0.95) mlm@cs.cmu.edu
  23.  *
  24.  * 20-Apr-89  C. Harald Koch (chk) at DCIEM Toronto.
  25.  *     Created.  chk@ben.dciem.dnd.ca
  26.  *
  27.  *================================================================
  28.  * based on ray2.c from DBW_Render, Copyright 1987 David B. Wecker
  29.  *
  30.  * From: chk@dretor.dciem.dnd.ca (C. Harald Koch)
  31.  * Subject: fbham.c - convert a 24bit FBM file to an IFF file using HAM mode
  32.  * To: Michael.Mauldin@nl.cs.cmu.edu (Michael Maudlin)
  33.  * Date: Mon, 1 May 89 17:21:16 EDT
  34.  * X-Mailer: ELM [version 2.2 PL0]
  35.  * 
  36.  * This is the source to my program to convert from a 24bit FBM file to Amiga
  37.  * HAM mode. It is based on Dave Wecker's RAY2 program, which converts the
  38.  * output of his raytracer to HAM mode. His code uses the Amiga graphics
  39.  * library to plot the pixels in a framebuffer for encoding; this version will
  40.  * run standalone.
  41.  * 
  42.  * There may be bugs, although it works well for me here. It is probably not in
  43.  * the format you want for FBM source; Please go ahead and modify it. There is
  44.  * probably room for some command line options for various things (such as the
  45.  * verbose output, run length encoding, etc) which I haven't needed and so
  46.  * haven't added.
  47.  * 
  48.  * I will send you the IRIS programs when I get them. Enjoy!
  49.  *    -chk
  50.  *****************************************************************/
  51.  
  52. #include <stdio.h>
  53. #include <math.h>
  54. #include "fbm.h"
  55.  
  56. # define USAGE "Usage: fbham < image > image"
  57.  
  58. #ifndef lint
  59. static char    *fbmid = 
  60. "$FBM fbham.c <1.0> 25-Jun-90  (C) 1989 by C. Harald Koch, source \
  61. code available free from MLM@CS.CMU.EDU and from UUNET archives$";
  62.  
  63. #endif
  64.  
  65. #define DEPTH        6        /* max depth of Amiga HAM image */
  66.  
  67. #define BMHDsize    20L        /* chunk sizes */
  68. #define CMAPsize    96L
  69. #define CAMGsize    4L
  70. #define BODYsize    ((long)(16000L))
  71. #define FORMsize    (BODYsize+CAMGsize+CMAPsize+BMHDsize+36L)
  72.  
  73. unsigned char *planes[DEPTH];    /* bitplane pointers */
  74.  
  75. static int      comp = 1;    /* compress image? */
  76. static int      depth = DEPTH;    /* depth of image being created */
  77. static int      colors = 16;    /* number of colors in color lookup table */
  78. static int      colorstats[4096][2];    /* color usage statistics */
  79. static int    row_size;    /* number of bytes in IFF row of data */
  80.  
  81. /* temp variables for writing IFF file */
  82. char            str[40];
  83. long            lng, pos1, pos2;
  84. short           wrd;
  85. unsigned char   byt;
  86. unsigned char  *dest, destbuf[BUFSIZ];
  87.  
  88.  
  89. main(argc, argv)
  90. char           *argv[];
  91. {
  92.     FBM             input;
  93.  
  94.     /* Clear the memory pointers so alloc_fbm won't be confused */
  95.     input.cm = input.bm = (unsigned char *) NULL;
  96.  
  97.     /* Read the image and rotate it */
  98.     if (!read_bitmap(&input, argc > 0 ? argv[1] : (char *) NULL))
  99.     {
  100.     exit(1);
  101.     }
  102.  
  103.     /* slight sanity checks. could be better. */
  104.     if (input.hdr.physbits != 8 || input.hdr.planes != 3) {
  105.     fprintf(stderr, "input file must be 24 bit RGB data\n");
  106.     exit(1);
  107.     }
  108.  
  109.     /* convert to HAM */
  110.     if (!fbm2ham(&input, stdout)) {
  111.     exit(1);
  112.     }
  113.  
  114.     exit(0);
  115. }
  116.  
  117.  
  118. /************************ run length encoding from Amiga RKM *****************/
  119. #define DUMP        0   /* list of different bytes */
  120. #define RUN        1   /* single run of bytes */
  121. #define MinRun        3   /* shortest allowed run */
  122. #define MaxRun        128 /* longest run (length is signed char) */
  123. #define    MaxDat        128 /* longest block of unencoded data */
  124. #define GetByte()    (*source++)
  125. #define PutByte(c)    { *dest++ = (c); ++PutSize; }
  126. #define OutDump(nn)    dest = PutDump(dest,nn);
  127. #define OutRun(nn,cc)    dest = PutRun(dest,nn,cc);
  128.  
  129. int             PutSize;
  130. char            buf[256];
  131.  
  132. unsigned char  *
  133. PutDump(dest, nn)
  134. unsigned char  *dest;
  135. int             nn;
  136. {
  137.     int             i;
  138.  
  139.     PutByte(nn - 1);
  140.     for (i = 0; i < nn; i++)
  141.     PutByte(buf[i]);
  142.     return (dest);
  143. }
  144.  
  145. unsigned char  *
  146. PutRun(dest, nn, cc)
  147. unsigned char  *dest;
  148. int             nn, cc;
  149. {
  150.     PutByte(-(nn - 1));
  151.     PutByte(cc);
  152.     return (dest);
  153. }
  154.  
  155. /* PackRow - pack a row of data using Amiga IFF RLE */
  156. int 
  157. PackRow(pSource, pDest, RowSize)
  158. unsigned char **pSource, **pDest;
  159. int             RowSize;
  160. {
  161.     unsigned char  *source, *dest;
  162.     char            c, lastc = '\000';
  163.     int             mode = DUMP, nbuf = 0,    /* number of chars in buf */
  164.                     rstart = 0;    /* buf index current run starts */
  165.  
  166.     source = *pSource;
  167.     dest = *pDest;
  168.  
  169.     PutSize = 0;
  170.     buf[0] = lastc = c = GetByte();
  171.     nbuf = 1;
  172.     RowSize--;
  173.  
  174.     for (; RowSize; --RowSize) {
  175.     buf[nbuf++] = c = GetByte();
  176.  
  177.     switch (mode) {
  178.     case DUMP:
  179.         if (nbuf > MaxDat) {
  180.         OutDump(nbuf - 1);
  181.         buf[0] = c;
  182.         nbuf = 1;
  183.         rstart = 0;
  184.         break;
  185.         }
  186.         if (c == lastc) {
  187.         if (nbuf - rstart >= MinRun) {
  188.             if (rstart > 0)
  189.             OutDump(rstart);
  190.             mode = RUN;
  191.         }
  192.         else if (rstart == 0)
  193.             mode = RUN;
  194.         }
  195.         else
  196.         rstart = nbuf - 1;
  197.         break;
  198.  
  199.     case RUN:
  200.         if ((c != lastc) || (nbuf - rstart > MaxRun)) {
  201.         OutRun((nbuf - 1) - rstart, lastc);
  202.         buf[0] = c;
  203.         nbuf = 1;
  204.         rstart = 0;
  205.         mode = DUMP;
  206.         }
  207.         break;
  208.     }
  209.     lastc = c;
  210.     }
  211.     switch (mode) {
  212.     case DUMP:
  213.     OutDump(nbuf);
  214.     break;
  215.     case RUN:
  216.     OutRun(nbuf - rstart, lastc);
  217.     break;
  218.     }
  219.  
  220.     *pSource = source;
  221.     *pDest = dest;
  222.  
  223.     return (PutSize);
  224. }
  225. /******************* end of RKM RL encoding routines **********************/
  226.  
  227.  
  228. /* build_histogram - count frequency of 12bit colors in an FBM image */
  229. build_histogram(image)
  230. FBM *image;
  231. {
  232.     int i, j, t0, t1, gap, val, used;
  233.     unsigned char *rp, *gp, *bp;
  234.     int diff;
  235.  
  236.     /* initialize color statistics. */
  237.     for (i = 0; i < 4096; i++) {
  238.     colorstats[i][0] = i;
  239.     colorstats[i][1] = 0;
  240.     }
  241.  
  242.     /* obtain pointers to the beginning of each plane (Red, Green, Blue) */
  243.     rp = image->bm;
  244.     gp = rp + image->hdr.plnlen;
  245.     bp = gp + image->hdr.plnlen;
  246.  
  247.     /* count the number of occurences of each color in the image */
  248.     for (i = 0 ; i < image->hdr.plnlen ; i++) {
  249.     val = ((*rp++ & 0xf0) << 4) + (*gp++ & 0xf0) + ((*bp++ & 0xf0) >> 4);
  250.     val %= 4096;
  251.     if (colorstats[val][1] < 32767) {
  252.         colorstats[val][1]++;
  253.     }
  254.     }
  255.  
  256.     /* sort the color stats in order of decreasing usage */
  257.     for (gap = 2048; gap > 0; gap /= 2) {
  258.     for (i = gap; i < 4096; i++) {
  259.         for (j = i - gap; j >= 0 &&
  260.             colorstats[j][1] < colorstats[j + gap][1]; j -= gap) {
  261.         t0 = colorstats[j][0];
  262.         t1 = colorstats[j][1];
  263.         colorstats[j][0] = colorstats[j + gap][0];
  264.         colorstats[j][1] = colorstats[j + gap][1];
  265.         colorstats[j + gap][0] = t0;
  266.         colorstats[j + gap][1] = t1;
  267.         }
  268.     }
  269.     }
  270.  
  271.     /* count the number of colors actually used in the image */
  272.     for (used = 0; used < 4096 && colorstats[used][1] > 0; used++);
  273.     fprintf(stderr, "Used %d colors out of a possible 4096\n", used);
  274. }
  275.  
  276. /*-
  277.  * plot_pixel - plot a color in an Amiga style bitmap (one plane per bit)
  278.  *
  279.  * Description:
  280.  *    A somewhat optimized routine to set/reset one bit in each plane
  281.  *    at the specified (x,y) coordinates. plane i gets the bit in
  282.  *    position i of color. a replacement for the Amiga WritePixel call.
  283. -*/
  284. plot_pixel(planes, x, y, color)
  285. unsigned char *planes[];
  286. int x, y;
  287. register int color;
  288. {
  289.     register int bit;
  290.     register unsigned char shifted_bit = 1 << (7-(x%8));
  291.     register int array_offset = y * row_size + x/8;
  292.     register int i;
  293.  
  294.     for (i = 0; color && i < depth; i++) {
  295.     bit = color & 1;
  296.     color >>= 1;
  297.  
  298.     if (bit)    *(planes[i] + array_offset) |= shifted_bit;
  299.     }
  300. }
  301.  
  302.  
  303. /*-
  304.  * fbm2ham - write an FBM image in HAM IFF format on the given file pointer.
  305.  *
  306. -*/
  307. fbm2ham(image, ofil)
  308. FBM *image;
  309. FILE *ofil;
  310. {
  311.     int             i, j, k, gap, t0, t1, prgb, crgb, cpix, ppix, maxdis, used;
  312.     int            c1, c2, diff;
  313.     unsigned char *rp, *gp, *bp;
  314.  
  315.     fprintf(stderr, "Building a color histogram:\n");
  316.     build_histogram(image);
  317.  
  318.     /* convert the image to an Amiga HAM bitplane based image */
  319.     cpix = 0;
  320.     crgb = colorstats[0][1];
  321.  
  322.     rp = image->bm;
  323.     gp = rp + image->hdr.plnlen;
  324.     bp = gp + image->hdr.plnlen;
  325.  
  326.     row_size = ((image->hdr.cols + 15) / 16) * 2;
  327.     diff = image->hdr.rowlen - image->hdr.cols;
  328.  
  329.     for (i = 0; i < depth ; i++) {
  330.     planes[i] = (unsigned char *)malloc(row_size * image->hdr.rows);
  331.     }
  332.  
  333.     for (i = 0; i < image->hdr.rows; i ++) {
  334.     /* verbose output because this program is slow even on a Sun */
  335.     if (i%50 == 0) { fprintf(stderr, "...%d", i); fflush(stderr); }
  336.  
  337.     /* step through current row, converting FBM pixel to nearest equivalent
  338.      * HAM pixel */
  339.     for (j = 0; j < image->hdr.cols; j++) {
  340.         prgb = crgb;
  341.         crgb = ((*rp++ & 0xf0) << 4) + (*gp++ & 0xf0) + ((*bp++ & 0xf0) >> 4);
  342.         crgb %= 4096;
  343.         ppix = cpix;
  344.  
  345.         /* start of scan line is ALWAYS an absolute color */
  346.         if (j == 0)
  347.         cpix = getcolor(ppix, &crgb, -1);
  348.         else
  349.         cpix = getcolor(ppix, &crgb, prgb);
  350.  
  351.         /* plot the computed pixel */
  352.         plot_pixel(planes, j, i, cpix);
  353.     }
  354.     rp += diff;
  355.     gp += diff;
  356.     bp += diff;
  357.     }
  358.     fprintf(stderr, "\n");
  359.  
  360.     /* Now we write the planes[] array we just created in ILBM format */
  361.     sprintf(str, "FORM");
  362.     fwrite(str, 1, 4, ofil);
  363.     pos1 = ftell(ofil);
  364.     lng = FORMsize;
  365.     fwrite(&lng, 4, 1, ofil);
  366.     sprintf(str, "ILBM");
  367.     fwrite(str, 1, 4, ofil);
  368.  
  369.     sprintf(str, "BMHD");
  370.     fwrite(str, 1, 4, ofil);
  371.     lng = BMHDsize;
  372.     fwrite(&lng, 4, 1, ofil);
  373.     wrd = image->hdr.cols;
  374.     fwrite(&wrd, 2, 1, ofil);    /* width */
  375.     wrd = image->hdr.rows;
  376.     fwrite(&wrd, 2, 1, ofil);    /* height */
  377.     wrd = 0;
  378.     fwrite(&wrd, 2, 1, ofil);    /* top */
  379.     wrd = 0;
  380.     fwrite(&wrd, 2, 1, ofil);    /* left */
  381.     byt = depth;
  382.     fwrite(&byt, 1, 1, ofil);    /* Depth */
  383.     byt = 0;
  384.     fwrite(&byt, 1, 1, ofil);    /* mask */
  385.     byt = comp;
  386.     fwrite(&byt, 1, 1, ofil);    /* compress */
  387.     byt = 0;
  388.     fwrite(&byt, 1, 1, ofil);    /* pad */
  389.     wrd = 0;
  390.     fwrite(&wrd, 2, 1, ofil);    /* transparency */
  391.     byt = 10;
  392.     fwrite(&byt, 1, 1, ofil);    /* aspect x */
  393.     byt = 11;
  394.     fwrite(&byt, 1, 1, ofil);    /* aspect y */
  395.     wrd = image->hdr.cols;
  396.     fwrite(&wrd, 2, 1, ofil);    /* page width */
  397.     wrd = image->hdr.rows;
  398.     fwrite(&wrd, 2, 1, ofil);    /* page height */
  399.  
  400.     /* CAMG chunk for displaying files on the Amiga. should include at least
  401.      * the HAM flag; I also add lace if the picture is large enough. Perhaps
  402.      * this should be an option. -CHK */
  403.     sprintf(str, "CAMG");
  404.     fwrite(str, 1, 4, ofil);
  405.     lng = CAMGsize;
  406.     fwrite(&lng, 4, 1, ofil);
  407.     if (image->hdr.rows > 200)
  408.     lng = 0x804L;        /* HAM | LACE */
  409.     else
  410.     lng = 0x800L;        /* HAM */
  411.     fwrite(&lng, 4, 1, ofil);
  412.  
  413.     /* write out the color lookup table */
  414.     sprintf(str, "CMAP");
  415.     fwrite(str, 1, 4, ofil);
  416.     lng = CMAPsize;
  417.     fwrite(&lng, 4, 1, ofil);
  418.     for (i = 0; i < 32; i++) {
  419.     str[0] = (colorstats[i][0] >> 4) & 0xF0;
  420.     str[1] = (colorstats[i][0]) & 0xF0;
  421.     str[2] = (colorstats[i][0] << 4) & 0xF0;
  422.     fwrite(str, 1, 3, ofil);
  423.     }
  424.  
  425.     sprintf(str, "BODY");
  426.     fwrite(str, 1, 4, ofil);
  427.     pos2 = ftell(ofil);
  428.     lng = BODYsize;
  429.     fwrite(&lng, 4, 1, ofil);
  430.  
  431.     lng = 0L;
  432.  
  433.     for (i = 0; i < image->hdr.rows; i ++) {
  434.     for (j = 0; j < depth; j++) {
  435.         if (comp) {
  436.         dest = destbuf;
  437.         wrd = PackRow(&planes[j], &dest, row_size);
  438.         lng += (long) wrd;
  439.         fwrite(destbuf, 1, wrd, ofil);
  440.         }
  441.         else {
  442.         fwrite(planes[j], 1, row_size, ofil);
  443.         planes[j] += row_size;
  444.         }
  445.     }
  446.     }
  447.  
  448.     /* make BODY a multiple of 4 bytes in length */
  449.     byt = 0;
  450.     if (comp)
  451.     while (lng % 4) {
  452.         fwrite(&byt, 1, 1, ofil);    /* pad */
  453.         lng++;
  454.     }
  455.  
  456.     if (comp) {
  457.     fseek(ofil, (long) pos2, 0);
  458.     fwrite(&lng, 4, 1, ofil);
  459.     lng -= BODYsize;
  460.     lng += FORMsize;
  461.     fseek(ofil, (long) pos1, 0);
  462.     fwrite(&lng, 4, 1, ofil);
  463.     }
  464.  
  465.     return 1;
  466. }
  467.  
  468. /* get the next encoding for a pixel, based on the previous pixel and the
  469.  * current 12 bit RGB value */
  470.  
  471. #define BPP        4        /* Bits per pixel */
  472. #define MAXGRAY        (1 << BPP)
  473. #define    ABS(x)        ((x) < 0 ? -(x) : (x))
  474.  
  475. /*-
  476.  *    first, check to see if pixel is the same color. if so, return
  477.  *    check if only one of R, G, B have changed. if so, return new pixel.
  478.  *    find closest entry in colormap. if exact match, return it.
  479.  *    modify one of R, G, B (whichever difference is largest)
  480.  *    compare previous calculation to best match in color table. return
  481.  *        whichever is a better match.
  482. -*/
  483. getcolor(ppix, crgb, prgb)
  484. int             ppix, *crgb, prgb;
  485. {
  486.     int             i, j, val, cr, cg, cb, pr, pg, pb, nr, ng, nb, best, dist, nrgb;
  487.  
  488.     /* if same color, then do a NOOP (same as previous pixel) */
  489.     if (*crgb == prgb)
  490.     return (ppix);
  491.  
  492.     /* set up for comparisons */
  493.     cb = *crgb & (MAXGRAY - 1);
  494.     cg = (*crgb >> BPP) & (MAXGRAY - 1);
  495.     cr = (*crgb >> (BPP * 2)) & (MAXGRAY - 1);
  496.  
  497.     pb = prgb & (MAXGRAY - 1);
  498.     pg = (prgb >> BPP) & (MAXGRAY - 1);
  499.     pr = (prgb >> (BPP * 2)) & (MAXGRAY - 1);
  500.  
  501.     /* see if only one plane changed, if so, use HAM encoding */
  502.     if (prgb != -1) {
  503.     if (pr == cr && pg == cg)
  504.         return (cb + 0x10);
  505.     if (pr == cr && pb == cb)
  506.         return (cg + 0x30);
  507.     if (pg == cg && pb == cb)
  508.         return (cr + 0x20);
  509.     }
  510.  
  511.     /* else look for an exact match in the color table (or minimal distance) */
  512.     for (i = 0; i < colors; i++) {
  513.     if (colorstats[i][0] == *crgb)
  514.         return (i);
  515.     if (i == 0) {
  516.         best = 0;
  517.         dist = distance(colorstats[i][0], *crgb);
  518.     }
  519.     else if ((j = distance(colorstats[i][0], *crgb)) < dist) {
  520.         best = i;
  521.         dist = j;
  522.     }
  523.     }
  524.  
  525.     /* do a forced absolute */
  526.     if (prgb == -1) {
  527.     *crgb = colorstats[best][0];
  528.     return (best);
  529.     }
  530.  
  531.     /* find which color is off the most from previous */
  532.     i = 0;
  533.     val = ABS(cr - pr);
  534.     if (ABS(cg - pg) > val) {
  535.     i = 1;
  536.     val = ABS(cg - pg);
  537.     }
  538.     if (ABS(cb - pb) > val) {
  539.     i = 2;
  540.     val = ABS(cb - pb);
  541.     }
  542.  
  543.     nr = pr;
  544.     ng = pg;
  545.     nb = pb;
  546.     switch (i) {
  547.     case 0:
  548.     nr = cr;
  549.     val = nr + 0x20;
  550.     break;
  551.     case 1:
  552.     ng = cg;
  553.     val = ng + 0x30;
  554.     break;
  555.     case 2:
  556.     nb = cb;
  557.     val = nb + 0x10;
  558.     break;
  559.     }
  560.     nrgb = (nr << (2 * BPP)) + (ng << BPP) + nb;
  561.  
  562.     /* now pick the best */
  563.     if (distance(nrgb, *crgb) < dist) {
  564.  
  565.     /* do a best relative */
  566.     *crgb = nrgb;
  567.     return (val);
  568.     }
  569.  
  570.     /* do a best absolute */
  571.     *crgb = colorstats[best][0];
  572.     return (best);
  573. }
  574.  
  575. /* calculate distance between two colors in 3D space */
  576. distance(argb, brgb)
  577. {
  578.     int             b, g, r;
  579.  
  580.     /* set up for comparisons */
  581.     b = argb & (MAXGRAY - 1);
  582.     g = (argb >> BPP) & (MAXGRAY - 1);
  583.     r = (argb >> (BPP * 2)) & (MAXGRAY - 1);
  584.  
  585.     b -= brgb & (MAXGRAY - 1);
  586.     g -= (brgb >> BPP) & (MAXGRAY - 1);
  587.     r -= (brgb >> (BPP * 2)) & (MAXGRAY - 1);
  588.  
  589.     return (r * r + g * g + b * b);
  590. }
  591.